#include "argumentosThread.h"
#include "concorrente.h"
#include "threads.h"
#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>




extern int NTHREADS;
extern long int *soma;
extern int *maior;
extern int *menor;
extern int dim;
//contador para controle da barreira
extern int cont;

//semaforos usados pelas threads
extern sem_t emCont;
extern sem_t *continua;

//funcoes que axam a soma da matriz , o seu maior elemento e se menor
static long int somaMatriz(int** mat, int tid);
static int maiorMatriz(int** mat, int tid);
static int menorMatriz(int** mat, int tid);

//Barreira para as threads
static void sinc_barreira();


//funcao que a thread ira rodar como sua main
void* run(void* arg){
    ArgumentosThread* argumentos = (ArgumentosThread*) arg;
    int tid = argumentos->tid;
    int **mat = argumentos->mat;
    int i, j;

    somaMatriz(mat, tid);
    maiorMatriz(mat, tid);
    menorMatriz(mat, tid);

    if(tid == 0){
        printf("Menor %d\n", menor[tid]);
        printf("Maior %d\n", maior[tid]);
        printf("Soma %ld\n", soma[tid]);
    }

    pthread_exit(NULL);

}


//implementacoes das funcoes prototipadas
static long int somaMatriz(int **mat, int tid){
    int meio = NTHREADS;
    soma[tid] = somaQuadrante(mat, tid, dim);
   
    while(meio != 1){
        sinc_barreira(tid);
	sem_wait(&emCont);
        if(meio % 2 != 0 && tid == 0){
            soma[tid] = soma[tid] + soma[meio - 1];
        }
        meio = meio/2;
        if (tid < meio){
            soma[tid] = soma[tid] + soma[tid + meio];
        }
        sem_post(&emCont);

    }
}

static int maiorMatriz(int** mat, int tid){
    int meio = NTHREADS;
    maior[tid] = maiorQuadrante(mat, tid, dim);
    while(meio != 1){
        sinc_barreira(tid);
        if(meio % 2 != 0 && tid == 0){
            maior[tid] = (maior[tid] > maior[meio - 1]) ? maior[tid] : maior[meio - 1];
        }
        meio = meio/2;
        if (tid < meio){
            maior[tid] = (maior[tid] > maior[tid + meio]) ? maior[tid] : maior[tid + meio];
        }

    }
}


static int menorMatriz(int** mat, int tid){
    int meio = NTHREADS;
    menor[tid] = menorQuadrante(mat, tid, dim);
    while(meio != 1){
        sinc_barreira(tid);
        if(meio % 2 != 0 && tid == 0){
            menor[tid] = (menor[tid] < menor[meio - 1]) ? menor[tid] : menor[meio - 1];
        }
        meio = meio/2;
        if (tid < meio){
            menor[tid] = (menor[tid] < menor[tid + meio]) ? menor[tid] : menor[tid + meio];
        }
    }
}

static void sinc_barreira(int tid){
    int i;
    sem_wait(&emCont);
    cont--;
    if(cont == 0){
        cont = NTHREADS;
        for(i = 0; i < NTHREADS; ++i){
            sem_post(&continua[i]);
        }
    }
    sem_post(&emCont);
    sem_wait(&continua[tid]);

}

